#include <mm/mem.h>
#include <kernel/kobject.h>
#include <kernel/initcall.h>
#include <kernel/syslog.h>
#include <drivers/platform.h>
#include <kernel/fdt.h>
#include <drivers/clock.h>
#include <drivers/gpio.h>


/**
 * @brief enable simple clock
 * @param clock: clock node
 * @param flag: flag
 * @return int 0 success, -1 failed
 */
static int simple_clock_enable(struct clock_node *clock, size_t flag)
{
    CLOCK_DEBUG_TRACE("enable %s", clock->name);
    *(volatile size_t *)clock->reg |= 1 << flag;
    return 0;
}


/**
 * @brief disable simple clock
 * @param clock: clock node
 * @param flag: flag
 * @return int 0 success, -1 failed
 */
static int simple_clock_disable(struct clock_node *clock, size_t flag)
{
    CLOCK_DEBUG_TRACE("disable %s", clock->name);
    *(volatile size_t*)clock->reg &= ~(1 << flag);
    return 0;
}


static struct clock_ops simple_clock_ops = {
    .enable = simple_clock_enable,
    .disable = simple_clock_disable,
};


/**
 * @brief simple clock probe
 * @param pdev: platform device
 * @return int 0 success, -1 failed
 */
static int simple_clock_probe(struct platform_device *pdev)
{
    SYSLOG_INFO("probe: %s", pdev->device.dt_node->name);
    
    fdt_node_t *node = pdev->device.dt_node;
    fdt_node_t *child = NULL;

    fdt_for_each_node_child(node, child) {
        CLOCK_SYSLOG_INFO("find clock %s", child->name);

        struct clock_node *clock = kmalloc(sizeof(struct clock_node));
        if(clock == NULL) {
            return -1;
        }

        clock->name = child->name;

        size_t reg = 0;
        if(fdt_read_prop_int(child, "reg", &reg)) {
            CLOCK_SYSLOG_ERROR("clock %s 'reg' not found", child->name);
            continue;
        }

        size_t freq = 0;
        if(fdt_read_prop_int(child, "freq", &freq)) {
            CLOCK_SYSLOG_ERROR("clock %s 'freq' not found", child->name);
            continue;
        }

        clock->reg = reg;
        clock->freq = freq;
        clock->status = 0;
        clock->ops = &simple_clock_ops;

        clock_node_add(clock);
    }

    return 0;
}


static struct of_device_id simple_clock_match[] = {
    {.compatible = "simple-clock"},
    {0}
};


static struct platform_driver simple_clock_pdrv = {
    .driver = {
        .name = "simple-clock",
        .of_match_table = simple_clock_match,
    },
    .probe = simple_clock_probe,
};


static int simple_clock_probe_register(void)
{
    return platform_driver_register(&simple_clock_pdrv);
}


base_initcall(simple_clock_probe_register);
